Skip to content

Conversation

@sadpandajoe
Copy link
Member

@sadpandajoe sadpandajoe commented Aug 19, 2025

SUMMARY

Problem

The Slice.get() method introduced in PR #29573 was causing a TypeError that broke chart thumbnail generation.

TypeError: Query.filter_by() takes 1 positional argument but 2 were given

Root Cause Analysis

The Bug

In superset/models/slice.py line 366, the code incorrectly used filter_by() with a BinaryExpression:

  # BROKEN
  qry = db.session.query(Slice).filter_by(id_or_uuid_filter(id_or_uuid))

Why It Failed

  • filter_by() expects keyword arguments: filter_by(id=123, name="test")
  • id_or_uuid_filter() returns a BinaryExpression object like Slice.id == 123
  • SQLAlchemy requirement: BinaryExpression objects must use filter(), not filter_by()

Pattern Comparison

The bug was introduced when copying the UUID support pattern from Dashboard.get(), but using the wrong filter method:

  # CORRECT (Dashboard.get() - already working)
  qry = db.session.query(Dashboard).filter(id_or_slug_filter(id_or_slug))

  # INCORRECT (Slice.get() - what was broken)  
  qry = db.session.query(Slice).filter_by(id_or_uuid_filter(id_or_uuid))

Solution

One-line fix: Change filter_by() to filter() in Slice.get()

  # FIXED
  qry = db.session.query(Slice).filter(id_or_uuid_filter(id_or_uuid))

Impact & Testing

What This Fixes

  • ✅ Chart thumbnail generation - cache_chart_thumbnail() in superset/tasks/thumbnails.py
  • ✅ All Slice.get() calls - Both ID and UUID string lookups now work
  • ✅ API endpoints - Any chart API calls that trigger Slice.get()

Comprehensive Test Coverage Added

Unit Tests (tests/unit_tests/models/slice_test.py - 5 tests):

  • Mock verification that filter() is called instead of filter_by()
  • Integration tests with real database calls for ID and UUID scenarios
  • Helper function tests for id_or_uuid_filter()

Integration Tests (tests/integration_tests/charts/api_tests.py - 4 tests):

  • test_slice_get_by_id() - Test with numeric ID strings
  • test_slice_get_by_uuid() - Test with UUID strings
  • test_slice_get_nonexistent_id() - Test error handling for invalid IDs
  • test_slice_get_nonexistent_uuid() - Test error handling for invalid UUIDs

Investigation Depth

  • ✅ Confirmed isolation: Only Slice.get() was broken, Dashboard.get() was already correct
  • ✅ No other models affected: Only charts/slices use this pattern
  • ✅ Screenshots analysis: Dashboard screenshots work (correct implementation), only chart screenshots were broken
  • ✅ Downloads unaffected: Export/download functionality uses different code paths

🤖 Generated with Claude Code

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

ADDITIONAL INFORMATION

  • Has associated issue: fixes: Screenshots cannot be generated with 6.0.0rc1 #34832
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

Fix TypeError in Slice.get() method introduced in PR #29573. The method was
incorrectly using filter_by() with a BinaryExpression from id_or_uuid_filter(),
which expects keyword arguments. Changed to use filter() which accepts
BinaryExpressions.

This fixes:
- Chart thumbnail generation failures
- 9 failing tests (8 event logger, 1 multi-tenant)
- All calls to Slice.get() with ID or UUID strings

Added comprehensive test coverage:
- Unit tests for Slice.get() method with mock verification
- Integration tests for both ID and UUID lookup scenarios
- Tests to prevent regression of filter_by usage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@dosubot dosubot bot added api Related to the REST API change:backend Requires changing the backend labels Aug 19, 2025
Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've completed my review and didn't find any issues.

Files scanned
File Path Reviewed
superset/models/slice.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

Fixed integration tests that were incorrectly passing UUID objects
instead of string representations to Slice.get() method:

1. test_slice_get_by_uuid: Changed chart.uuid to str(chart.uuid)
2. test_slice_get_nonexistent_uuid: Changed invalid UUID string
   "non-existent-uuid-123" to proper UUID format using uuid.uuid4()

The id_or_uuid_filter function was correctly designed to accept
string inputs (id_or_uuid: str), but our tests were passing wrong
data types. This fixes the GitHub Actions CI failures.

Addresses issues identified in PR #34769 CI logs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

This comment was marked as outdated.

@sadpandajoe sadpandajoe requested a review from Copilot August 20, 2025 06:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a TypeError in the Slice.get() method that was preventing chart thumbnail generation and breaking API calls. The bug was caused by incorrectly using filter_by() with a BinaryExpression when filter() should have been used.

  • Fixed the SQLAlchemy query method from filter_by() to filter() in Slice.get()
  • Added comprehensive unit tests to verify the fix and prevent regression
  • Added integration tests to ensure real database scenarios work correctly

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
superset/models/slice.py One-line fix changing filter_by() to filter() in Slice.get() method
tests/unit_tests/models/slice_test.py New unit test file with 5 tests covering mock verification and helper function testing
tests/integration_tests/charts/api_tests.py Added 4 integration tests for real database scenarios with ID/UUID lookups

for test_input in test_cases:
try:
result = Slice.get(test_input)
# Success - no TypeError occurred, result can be None or a Slice. # noqa: E501
Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comment formatting is inconsistent with the period placement before the noqa comment. Consider moving the period after the noqa comment or removing it entirely for better readability.

Suggested change
# Success - no TypeError occurred, result can be None or a Slice. # noqa: E501
# Success - no TypeError occurred, result can be None or a Slice # noqa: E501.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The # no qa is not part of the comment instead it's so we don't get an error saying the length is too long.

# Should return a BinaryExpression that can be used with filter()
assert result is not None
# The important thing is it doesn't crash and returns a filter expression. # noqa: E501

Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The same comment is duplicated in both test methods. Consider extracting this into a shared docstring or making the comments more specific to each test case.

Suggested change

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we remove a line break here. That would leave 0 breaks between this and the next test.

result = id_or_uuid_filter(test_uuid)
# Should return a BinaryExpression that can be used with filter()
assert result is not None
# The important thing is it doesn't crash and returns a filter expression. # noqa: E501
Copy link

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The same comment is duplicated in both test methods. Consider extracting this into a shared docstring or making the comments more specific to each test case.

Suggested change
# The important thing is it doesn't crash and returns a filter expression. # noqa: E501

Copilot uses AI. Check for mistakes.
rv = self.get_assert_metric(uri, "get")
assert rv.status_code == 404

def test_slice_get_by_id(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we merge these tests together and avoid duplication? both for the success and non existent ones

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying combine the id ones together and the uuid ones together? I normally prefer them separate in case if one use case fails and the other one passes, but can see that the uuid one might not actually be testing anything if we aren't in the if statement clause.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I mean, looking at the tests you can assert both things just with 1 extra assertion that's why I suggested merging them, but the ultimate goal is to DRY them because those tests are 95% the same

from superset.models.slice import id_or_uuid_filter, Slice


class TestSlice:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment from above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Antonio-RiveroMartnez good call. Parameterized it so I'd still know if one of the use cases failed but no duplicated code.

Address PR reviewer feedback by consolidating duplicate test cases
into parameterized tests while maintaining test isolation and clarity.

Changes:
- Unit tests: 5 separate tests → 3 parameterized tests (8 test cases)
- Integration tests: 4 separate tests → 2 parameterized tests (4 test cases)
- Fixed UUID no-op issue: Use pytest.skip() if chart.uuid is None
- Added uuid import to integration tests

Benefits:
- Reduced code duplication significantly
- Each parameter still runs as separate test case
- Clear failure identification with descriptive test names
- Easier to add new test scenarios
- Better maintainability

Test coverage remains comprehensive:
- Mock-based contract testing for filter() vs filter_by()
- Integration testing with real database calls
- Error handling for non-existent IDs/UUIDs
- BinaryExpression testing for id_or_uuid_filter function

Addresses feedback from Antonio-RiveroMartnez while preserving
the test isolation benefits originally preferred.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@rusackas rusackas added the cares:preset Preset cares about this issue label Aug 20, 2025
Copy link
Member

@Antonio-RiveroMartnez Antonio-RiveroMartnez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@sadpandajoe sadpandajoe merged commit 9de1706 into master Aug 20, 2025
55 checks passed
@sadpandajoe sadpandajoe deleted the fix/slice-get-filter-by-bug branch August 20, 2025 18:22
@sadpandajoe sadpandajoe moved this from New to Cherried in Apache Superset 6.0.X Release Bugs Aug 20, 2025
sadpandajoe added a commit that referenced this pull request Aug 20, 2025
…BinaryExpression (#34769)

Co-authored-by: Claude <[email protected]>
Co-authored-by: Copilot <[email protected]>
(cherry picked from commit 9de1706)
@sadpandajoe sadpandajoe added v6.0 Label added by the release manager to track PRs to be included in the 6.0 branch and removed 6.0 labels Aug 26, 2025
@github-actions github-actions bot added 🍒 6.0.0 Cherry-picked to 6.0.0 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels labels Dec 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels cares:preset Preset cares about this issue change:backend Requires changing the backend size/L v6.0 Label added by the release manager to track PRs to be included in the 6.0 branch 🍒 6.0.0 Cherry-picked to 6.0.0

Projects

Development

Successfully merging this pull request may close these issues.

Screenshots cannot be generated with 6.0.0rc1

3 participants